/*****************************************************************************
 * Copyright (C) Codehaus.org                                                *
 * ------------------------------------------------------------------------- *
 * Licensed under the Apache License, Version 2.0 (the "License");           *
 * you may not use this file except in compliance with the License.          *
 * You may obtain a copy of the License at                                   *
 *                                                                           *
 * http://www.apache.org/licenses/LICENSE-2.0                                *
 *                                                                           *
 * Unless required by applicable law or agreed to in writing, software       *
 * distributed under the License is distributed on an "AS IS" BASIS,         *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  *
 * See the License for the specific language governing permissions and       *
 * limitations under the License.                                            *
 *****************************************************************************/
package com.googlecode.lazyparsec;

/**
 * Provides common token values.
 *
 * @author Ben Yu
 */
public final class Tokens {

    private Tokens() {
    }

    /**
     * Returns a {@link Fragment} tagged with {@code tag}.
     *
     * @param text the fragment text.
     * @param tag  the tag representing the fragment's semantics.
     * @return the TypedToken object.
     */
    public static Fragment fragment(String text, Object tag) {
        return new Fragment(text, tag);
    }

    /**
     * Returns a {@link Fragment} tagged as {@link Tag#RESERVED}.
     *
     * @param name the reserved word.
     * @return the token value.
     */
    public static Fragment reserved(String name) {
        return fragment(name, Tag.RESERVED);
    }

    /**
     * Returns a {@link Fragment} tagged as {@link Tag#IDENTIFIER}.
     *
     * @param name the identifier.
     * @return the token value.
     */
    public static Fragment identifier(String name) {
        return fragment(name, Tag.IDENTIFIER);
    }

    /**
     * Returns a {@link Fragment} tagged as {@link Tag#DECIMAL}.
     *
     * @param s the decimal string representation.
     * @return the token value.
     */
    public static Fragment decimalLiteral(String s) {
        return fragment(s, Tag.DECIMAL);
    }

    /**
     * Returns a {@link Fragment} tagged as {@link Tag#INTEGER}.
     *
     * @param s the integer string representation.
     * @return the token value.
     */
    public static Fragment integerLiteral(String s) {
        return fragment(s, Tag.INTEGER);
    }

    /**
     * Returns a {@link ScientificNotation} with {@code significand} before the 'e' or 'E'
     * and {@code exponent} after.
     */
    public static ScientificNotation scientificNotation(String significand, String exponent) {
        return new ScientificNotation(significand, exponent);
    }

    /**
     * Represents a fragment tagged according to its semantics.
     */
    public static final class Fragment {
        private final String text;
        private final Object tag;

        public Fragment(String text, Object tag) {
            this.text = text;
            this.tag = tag;
        }

        /**
         * Returns the text of the token value.
         */
        public String text() {
            return text;
        }

        /**
         * Returns the tag of the token value.
         */
        public Object tag() {
            return tag;
        }

        boolean equalFragment(Fragment that) {
            return tag.equals(that.tag) && text.equals(that.text);
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Fragment) {
                return equalFragment((Fragment) obj);
            } else return false;
        }

        @Override
        public int hashCode() {
            return tag.hashCode() * 31 + text.hashCode();
        }

        @Override
        public String toString() {
            return text;
        }
    }

    /**
     * Represents a scientific notation with a significand (mantissa) and an exponent. Both are
     * represented with a {@link String} to avoid number range issue.
     */
    public static final class ScientificNotation {

        /**
         * The significand (mantissa) before the "E".
         */
        public final String significand;

        /**
         * The exponent after the "E".
         */
        public final String exponent; // we leave the range check to the semantics analysis

        public ScientificNotation(String mantissa, String exp) {
            this.significand = mantissa;
            this.exponent = exp;
        }

        @Override
        public String toString() {
            return significand + "E" + exponent;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof ScientificNotation) {
                ScientificNotation that = (ScientificNotation) obj;
                return significand.equals(that.significand) && exponent.equals(that.exponent);
            }
            return false;
        }

        @Override
        public int hashCode() {
            return significand.hashCode() * 31 + exponent.hashCode();
        }
    }

    /**
     * Pre-built {@link Fragment} token tags.
     */
    public enum Tag {

        /**
         * Reserved word
         */
        RESERVED,

        /**
         * Regular identifier
         */
        IDENTIFIER,

        /**
         * Integral number literal
         */
        INTEGER,

        /**
         * Decimal number literal
         */
        DECIMAL
    }
}

